using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections.Specialized;
using System.Reflection;
using Microsoft.Win32;
using EnvDTE;
using EnvDTE80;

namespace NUnitit
{
    public static class Common
    {
        /// <summary>
        /// Converts a relative path to an absolute path.
        /// </summary>
        /// <param name="basePath">The base path.</param>
        /// <param name="relativePath">The relative path.</param>
        /// <returns></returns>
        public static string MakeAbsolutePath(string basePath, string relativePath)
        {
            if (string.IsNullOrEmpty(basePath))
                throw new ArgumentNullException("basePath");

            if (string.IsNullOrEmpty(relativePath))
                throw new ArgumentNullException("relativePath");

            if (Path.IsPathRooted(relativePath))
                return relativePath;

            return Path.Combine(basePath, relativePath);
        }

        /// <summary>
        /// Creates a relative path.
        /// </summary>
        /// <param name="basePath">The base path.</param>
        /// <param name="targetPath">The target path.</param>
        /// <returns>The relative path</returns>
        public static string MakeRelativePath(string basePath, string targetPath)
        {
            if (string.IsNullOrEmpty(basePath))
                throw new ArgumentNullException("basePath");

            if (string.IsNullOrEmpty(targetPath))
                throw new ArgumentNullException("targetPath");

            bool isRooted = Path.IsPathRooted(basePath) && Path.IsPathRooted(targetPath);

            if (isRooted)
            {
                bool isDifferentRoot = string.Compare(Path.GetPathRoot(basePath),
                                                      Path.GetPathRoot(targetPath), true) != 0;

                if (isDifferentRoot)
                    return targetPath;
            }

            StringCollection relativePath = new StringCollection();

            string[] fromDirectories = basePath.Split(Path.DirectorySeparatorChar);
            string[] toDirectories = targetPath.Split(Path.DirectorySeparatorChar);

            int length = Math.Min(fromDirectories.Length, toDirectories.Length);
            int lastCommonRoot = -1;

            // find common root

            for (int x = 0; x < length; x++)
            {
                if (string.Compare(fromDirectories[x], toDirectories[x], true) != 0)
                    break;

                lastCommonRoot = x;
            }

            if (lastCommonRoot == -1)
                return targetPath;

            // add relative folders in from path
            for (int x = lastCommonRoot + 1; x < fromDirectories.Length; x++)
                if (fromDirectories[x].Length > 0)
                    relativePath.Add("..");

            // add to folders to path
            for (int x = lastCommonRoot + 1; x < toDirectories.Length; x++)
                relativePath.Add(toDirectories[x]);

            // create relative path
            string[] relativeParts = new string[relativePath.Count];

            relativePath.CopyTo(relativeParts, 0);
            string newPath = string.Join(Path.DirectorySeparatorChar.ToString(), relativeParts);

            return newPath;
        }

        /// <summary>
        /// Gets the assembly file attribute.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns></returns>
        public static AssemblyFileVersionAttribute GetAssemblyFileAttribute(Assembly assembly)
        {
            // make sure we were passed an assy
            if (null == assembly)
                return null;

            // try and get the requested assy attribute
            object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), true);

            if (null == attributes)
                return null;

            return (AssemblyFileVersionAttribute)attributes[0];
        }

        /// <summary>
        /// Gets the NUnit install path.
        /// </summary>
        /// <returns></returns>
        public static string GetNUnitInstallDir()
        {
            string ret = null;

            using (RegistryKey keyNUnit = Registry.CurrentUser.OpenSubKey("software\\nunit.org\\nunit", false))
            {
                if (keyNUnit != null)
                {
                    string[] versions = keyNUnit.GetSubKeyNames();

                    if (versions != null && versions.Length > 0)
                    {
                        string version = versions[0];

                        foreach (string v in versions)
                        {
                            if (string.Compare(v, version) > 0)
                                version = v;
                        }

                        using (RegistryKey keyVersion = keyNUnit.OpenSubKey(version, false))
                        {
                            if (keyVersion != null)
                            {
                                ret = (string)keyVersion.GetValue("InstallDir");
                            }
                        }
                    }
                }
            }

            return ret;
        }

        public static string GetNUnitExecPath(string exec)
        {
            string ret = null;

            string installDir = GetNUnitInstallDir();

            if (!string.IsNullOrEmpty(installDir))
            {
                installDir = Path.Combine(installDir, "bin");
                ret = Path.Combine(installDir, exec);
            }
                
            return ret;
        }

        public static string GetProjectOutputFile(Project project)
        {
            string ret = null;

            string projectDirectory = Path.GetDirectoryName(project.FileName);
            string outputDirectory = (string)project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value;
            string outputFile = (string)((object[])project.ConfigurationManager.ActiveConfiguration.OutputGroups.Item("Primary output").FileNames)[0];

            ret = Path.Combine(projectDirectory, Path.Combine(outputDirectory, outputFile));

            return ret;
        }

        /// <summary>
        /// Gets the startup project for the given solution.
        /// </summary>
        /// <param name="solution">The solution.</param>
        /// <returns><c>null</c> if the startup project cannot be found.</returns>
        public static Project GetStartupProject(Solution solution)
        {
            Project ret = null;

            if (solution != null &&
               solution.SolutionBuild != null &&
               solution.SolutionBuild.StartupProjects != null)
            {
                string uniqueName = (string)((object[])solution.SolutionBuild.StartupProjects)[0];

                // Can't use the solution.Item(uniqueName) here since that doesn't work
                // for projects under solution folders.

                ret = GetProject(solution, uniqueName);
            }

            return ret;
        }

        /// <summary>
        /// Gets the project located in the given solution.
        /// </summary>
        /// <param name="solution">The solution.</param>
        /// <param name="uniqueName">The unique name of the project.</param>
        /// <returns><c>null</c> if the project could not be found.</returns>
        public static Project GetProject(Solution solution, string uniqueName)
        {
            Project ret = null;

            if (solution != null && uniqueName != null)
            {
                foreach (Project p in solution.Projects)
                {
                    ret = GetSubProject(p, uniqueName);

                    if (ret != null)
                        break;
                }
            }

            return ret;
        }

        /// <summary>
        /// Gets a project located under another project item.
        /// </summary>
        /// <param name="project">The project to start the search from.</param>
        /// <param name="uniqueName">Unique name of the project.</param>
        /// <returns><c>null</c> if the project can't be found.</returns>
        /// <remarks>Only works for solution folders.</remarks>
        public static Project GetSubProject(Project project, string uniqueName)
        {
            Project ret = null;

            if(project != null)
            {
                if (project.UniqueName == uniqueName)
                {
                    ret = project;
                }
                else if (project.Kind == Constants.vsProjectKindSolutionItems)
                {
                    // Solution folder

                    foreach (ProjectItem projectItem in project.ProjectItems)
                    {
                        ret = GetSubProject(projectItem.SubProject, uniqueName);

                        if (ret != null)
                            break;
                    }
                }
            }

            return ret;
        }

#if DEBUG
        public static void DumpProperties(Properties properties)
        {
            for (int i = 1; i <= properties.Count; i++)
            {
                string name = "unknown";
                string value = "unknown";
                try
                {
                    name = (string)properties.Item(i).Name;
                    value = (string)properties.Item(i).Value;
                }
                catch 
                {
                }

                System.Diagnostics.Debug.WriteLine(i.ToString() + ": " + name + " : " + value);
            }
        }

        public static void DumpProjectProperties(Project project)
        {
            DumpProperties(project.Properties);

            
        }
#endif
    }
}
